理解 WebAssembly 文本格式 您所在的位置:网站首页 electron wasm 理解 WebAssembly 文本格式

理解 WebAssembly 文本格式

2023-09-01 13:38| 来源: 网络整理| 查看: 265

上面的例子是一个相当简单的日志函数:它只是打印一个整数!要是我们想输出一个文本字符串呢?为了处理字符串及其他复杂数据类型,WebAssembly 提供了内存。

按照 WebAssembly 的定义,内存就是一个随着时间增长的字节数组。WebAssembly 包含诸如 i32.load 和 i32.store 指令来实现对线性内存的读写。

从 JavaScript 的角度来看,内存就是一个 ArrayBuffer,并且它是可变大小的。从字面上来说,这也是 asm.js 所做的(除了它不能改变大小;参考 asm.js编程模型)。

因此,一个字符串就是位于这个线性内存某处的字节序列。

让我们假设我们已经把一个合适的字符串字节写入到了内存中;那么,我们该如何把那个字符串传递给 JavaScript 呢?

关键在于 JavaScript 能够通过WebAssembly.Memory()接口创建 WebAssembly 线性内存实例,并且能够通过相关的实例方法获取已经存在的内存实例(当前每一个模块实例只能有一个内存实例)。内存实例拥有一个buffer (en-US)获取器,它返回一个指向整个线性内存的 ArrayBuffer。

内存实例也能够增长。举例来说,在 JavaScript 中可以调用Memory.grow() (en-US)方法。由于 ArrayBuffer 不能改变大小,所以,当增长产生的时候,当前的 ArrayBuffer 会被移除,并且一个新的 ArrayBuffer 会被创建并指向新的、更大的内存。这意味着为了向 JavaScript 传递一个字符串,我们所需要做的就是把字符串在线性内存中的偏移量,以及表示其长度的方法传递出去。

虽然有许多不同的方法在字符串自身当中保存字符串的长度(例如,C 字符串);但是,这里为了简单起见,我们仅仅把偏移量和长度都作为参数:

wasm

(import "console" "log" (func $log (param i32) (param i32)))

在 JavaScript 端,我们可以使用文本解码器 API,轻松地把我们的字节解码转化为一个 JavaScript 字符串。(这里,我们使用 utf8,不过,许多其他编码也是支持的。)

js

consoleLogString(offset, length) { var bytes = new Uint8Array(memory.buffer, offset, length); var string = new TextDecoder('utf8').decode(bytes); console.log(string); }

这个谜题的最后一部分就是 consoleLogString 从哪里获得?WebAssembly 的内存(memory)实例。这里,WebAssembly 给我们很大灵活性:我们既可以使用 JavaScript 创建一个内存对象,让 WebAssembly 模块导入这个内存,或者我们让 WebAssembly 模块创建这个内存并把它导出给 JavaScript。

为了简单起见,让我们用 JavaScript 创建它,然后把它导入到 WebAssembly。我们的导入语句编写如下:

wasm

(import "js" "mem" (memory 1))

1 表示导入的内存必须至少有 1 页内存。(WebAssembly 定义一页为 64KB。)

因此,让我们看一个完整的打印字符串“Hi”的模块。在一个常规的已编译的 C 程序,你会调用一个函数来为字符串分配一段内存。但是,因为我们正在编写自己的汇编,并且我们拥有整个线性内存,所以,我们可以使用数据(data)段把字符串内容写入到一个全局内存中。数据段允许字符串字节在实例化时被写在一个指定的偏移量。而且,它与原生的可执行格式中的数据(.data)段是类似的。

我们最终的 wasm 模块看起来像这样:

wasm

(module (import "console" "log" (func $log (param i32 i32))) (import "js" "mem" (memory 1)) (data (i32.const 0) "Hi") (func (export "writeHi") i32.const 0 ;; pass offset 0 to log i32.const 2 ;; pass length 2 to log call $log))

备注: 注意上面的双分号语法,它允许在 WebAssembly 文件中添加注释。

现在,我们可以从 JavaScript 中创建一个 1 页的内存(Memory)然后把它传递进去。这会在控制台输出"Hi"。

js

var memory = new WebAssembly.Memory({ initial: 1 }); var importObj = { console: { log: consoleLogString }, js: { mem: memory } }; fetchAndInstantiate("logger2.wasm", importObject).then(function (instance) { instance.exports.writeHi(); });

备注: 你可以在 GitHub 上找到完整源代码logger2.html (或者实时运行)。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有